package procilege.impl.eval;

import java.util.HashMap;
import java.util.Map;

import procilege.compute.ExprEvaluation;
import procilege.compute.ExprEvaluationTraveller;
import procilege.compute.PlaceTypeMetadata;
import procilege.compute.PlaceType;
import procilege.compute.TravelMap;

import com.google.inject.Inject;
import com.google.inject.Injector;

public class ExprEvaluationImpl implements ExprEvaluation,
		TravelMap<ExprEvaluationTraveller> {

	@Inject
	private Injector injector;

	private Map<Class<?>, PlaceType<ExprEvaluationTraveller>> map;

	public synchronized void configure() {

		map = new HashMap<Class<?>, PlaceType<ExprEvaluationTraveller>>();

		register(BoolExprAndEvalPlace.class);
		register(BoolExprOrEvalPlace.class);
		register(PredicateEntityEvalPlace.class);
		register(PredicateEntityPropertyEvalPlace.class);
		register(PredicateFunctionEvalPlace.class);
		register(PredicateObjectPropertyEvalPlace.class);
		register(PredicatePrivAvailiabilityEvalPlace.class);
		register(PredicateValueEvalPlace.class);
		register(RelationalEvalPlace.class);
		register(RelationConformEvalPlace.class);
	}

	private void register(
			Class<? extends PlaceType<ExprEvaluationTraveller>> oprcls) {

		PlaceTypeMetadata ot = oprcls.getAnnotation(PlaceTypeMetadata.class);
		Class<?> target = ot.type();
		try {
			PlaceType<ExprEvaluationTraveller> op = oprcls.newInstance();
			injector.injectMembers(op);
			map.put(target, op);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public PlaceType<ExprEvaluationTraveller> find(Class<?> cls) {

		if (map == null) {
			configure();
		}

		return map.get(cls);
	}

	public Object eval(Object expr) {

		ExprEvaluationTravellerImpl tr = new ExprEvaluationTravellerImpl(this);
		injector.injectMembers(tr);
		tr.setout(expr);
		return tr.getPreviousVisitResult();
	}
}
